<script lang="ts" setup>
import type { FolderProps, TabsProps } from '@/types';

import {
  type ComponentInternalInstance,
  computed,
  defineAsyncComponent,
  getCurrentInstance,
  hydrateOnVisible,
  nextTick,
  onBeforeUnmount,
  onMounted,
  onUnmounted,
  ref,
  useTemplateRef,
  watch,
} from 'vue';

import {
  ElEmpty,
  ElMessage,
  ElMessageBox,
  ElTabPane,
  ElTabs,
  type TabPaneName,
  type TabsPaneContext,
} from 'element-plus';
import { customAlphabet } from 'nanoid';

import { useAppStore } from '@/store';
import { splitChar } from '@/utils';

defineOptions({
  name: 'SystemNotepad',
});
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10);
const Toolbar = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('./components/notepad/Toolbar.vue'),
});
const CodeEditor = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('@/components/core/coding-editor/src/CodeEditor.vue'),
});
const SelectByFileType = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('../_core/common/SelectByFileType.vue'),
});
const SaveTo = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () => import('../_core/common/SaveTo.vue'),
});
const MonacoEditorSetting = defineAsyncComponent({
  hydrate: hydrateOnVisible(),
  loader: () =>
    import('@/components/core/coding-editor/src/components/MonacoEditorSetting.vue'),
});
const moreSettingElRef = useTemplateRef('moreSettingElRef');
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const baseUrl = proxy?.$config.baseUrl;
const appStore = useAppStore();
const loading = ref(false);
const saveToRef = useTemplateRef('saveToRef');
const selectByFileTypeRef = useTemplateRef('selectByFileTypeRef');
const documentHeight = ref(0);
const documentElRef = useTemplateRef('documentElRef');
const setReact = () => {
  nextTick(() => {
    setTimeout(() => {
      if (documentElRef.value) {
        documentHeight.value = documentElRef.value.offsetHeight;
      }
    }, 500);
  });
};
const editorRefs = ref<Record<string, any>>({});
const setEditorRef = (el: any, name: string) => {
  if (el) {
    editorRefs.value[`${name}`] = el;
  }
};
const editableTabsValue = ref<number | string | undefined>();
const editableTabs = ref<TabsProps[]>([]);
const currentTabPane = computed(() => {
  return editableTabs.value.find(
    (tab: { name: any }) => tab.name === editableTabsValue.value,
  );
});
// 新建文件
const onNewFile = () => {
  ElMessageBox.prompt('请输入文件名称', '新建文件', {
    buttonSize: 'small',
    cancelButtonText: '取消',
    confirmButtonText: '确定',
    customClass: 'wos-common-confirm',
    draggable: true,
    inputErrorMessage: '无效的名称，请重新输入',
    inputPattern: /^[^"%*/:<>?\\|]+$/,
    inputPlaceholder: '请输入文件名称',
    inputValue: '新建 文本文档.txt',
  })
    .then(({ value }: { value: string }) => {
      const tab = {
        content: '',
        ext: value.split('.').pop() || '',
        isNewFile: true,
        label: value,
        name: nanoid(),
        path: '',
      };
      editableTabs.value.push(tab);
      editableTabsValue.value = tab.name;
    })
    .catch(() => {
      console.log('取消');
    });
};
// 移除tab
const removeTab = (targetName: TabPaneName) => {
  const tabs = editableTabs.value;
  let activeName = editableTabsValue.value;
  if (activeName === targetName) {
    tabs.forEach((tab: { name: TabPaneName }, index: number) => {
      if (tab.name === targetName) {
        const nextTab = tabs[index + 1] || tabs[index - 1];
        if (nextTab) {
          activeName = nextTab.name;
        }
      }
    });
  }
  editorRefs.value[`${targetName}`]?.dispose(targetName);
  editableTabsValue.value = activeName;
  editableTabs.value = tabs.filter(
    (tab: { name: TabPaneName }) => tab.name !== targetName,
  );
};
// 点击tab
const clickTab = (tab: TabsPaneContext) => {
  const { paneName } = tab;
  editableTabsValue.value = paneName;
};
// 保存文件到指定目录
const saveFile = (action: string = 'overwrite') => {
  const ctp = currentTabPane.value;
  if (!ctp) return;
  loading.value = true;
  try {
    const postData = { content: ctp.content, savePath: ctp.path };
    const url = `${baseUrl}/file/save?action=${action}`;
    fetch(url, {
      body: JSON.stringify(postData),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
      },
      method: 'POST',
    })
      .then((response) => response.json())
      .then((res) => {
        const { code, data, msg } = res;
        if (code === 200) {
          ctp.isNewFile = false;
          ctp.label = data?.fileName;
          ctp.path = data?.fullPath;
          ElMessage.success({
            message: msg || '保存成功',
            type: 'success',
          });
        } else {
          ElMessage.error(msg || '获取文件夹目录失败');
        }
      });
  } catch (error) {
    console.log(error);
    ElMessage.error('请求发生了错误，请稍后重试');
  } finally {
    loading.value = false;
  }
};
const buildFilePath = async (data: {
  path?: string;
  res?: { action: string; fileName: string };
}) => {
  const ctp = currentTabPane.value;
  if (ctp) {
    ctp.label = data.res?.fileName || ctp.label;
    ctp.path = `${data.path}${splitChar}${ctp.label}`;
  }
  saveFile(data.res?.action);
};
// 菜单指令
const onCommand = (command: string) => {
  const ctp = currentTabPane.value;
  switch (command) {
    case 'newFile': {
      onNewFile();
      break;
    }
    case 'openFile': {
      selectByFileTypeRef.value?.open({ title: '选择需要打开的文件' });
      break;
    }
    case 'saveAs': {
      // 新文件检查文件夹内相同文件名是否存在
      saveToRef.value?.open({
        callback: buildFilePath,
        checkName: ctp?.label,
        title: '选择文件保存的文件夹',
      });
      break;
    }
    case 'saveFile': {
      if (ctp?.isNewFile) {
        // 新文件检查文件夹内相同文件名是否存在
        saveToRef.value?.open({
          callback: buildFilePath,
          checkName: ctp?.label,
          title: '选择文件保存的文件夹',
        });
      } else {
        saveFile();
      }
      break;
    }
    case 'setting': {
      // 新文件检查文件夹内相同文件名是否存在
      moreSettingElRef.value?.open();
      break;
    }
    // No default
  }
};
// 另存为
const onSaveToConfirm = (data: { targetPath: string }) => {
  const moveList: string[] = [];
  loading.value = true;
  try {
    const postData = { files: moveList, target: data.targetPath };
    const url = `${baseUrl}/folder/moveto`;
    fetch(url, {
      body: JSON.stringify(postData),
      headers: {
        'Content-Type': 'application/json;charset=utf-8',
      },
      method: 'POST',
    })
      .then((response) => response.json())
      .then((res) => {
        const { code, msg } = res;
        if (code === 200) {
          ElMessage.success({
            message: msg || '保存成功',
            type: 'success',
          });
        } else {
          ElMessage.error(msg || '获取文件夹目录失败');
        }
      });
  } catch (error) {
    console.log(error);
    ElMessage.error('请求发生了错误，请稍后重试');
  } finally {
    loading.value = false;
  }
};
const onSaveToCancel = () => {
  console.log('取消另存为');
};
// 选择文件
const onConfirm = (item: FolderProps) => {
  const { ext, fullPath, name } = item;
  loading.value = true;
  try {
    const url = `${baseUrl}/file/load?path=${fullPath}`;
    fetch(url, {
      method: 'GET',
    })
      .then((response) => response.text())
      .then((res) => {
        let tab = editableTabs.value.find(
          (item: { path: string }) => item.path === fullPath,
        );
        if (tab) {
          Object.assign(tab, {
            content: res,
            label: item.name,
            path: fullPath,
          });
        } else {
          tab = {
            content: res,
            ext,
            isNewFile: false,
            label: name,
            name: nanoid(),
            path: fullPath,
          };
          editableTabs.value.push(tab);
        }
        editableTabsValue.value = tab?.name;
      });
  } catch (error) {
    console.log(error);
    ElMessage.error('请求发生了错误，请稍后重试');
  } finally {
    loading.value = false;
  }
};

const handleEvent = async (event: any) => {
  if (event.keyCode === 83 && event.ctrlKey && event.code === 'KeyS') {
    event.preventDefault();
    event.returnValue = false;
    onCommand('saveFile');
  }
};
onMounted(() => {
  window.addEventListener('keydown', handleEvent);
});
onBeforeUnmount(() => {
  editableTabs.value.forEach((item: any) => {
    editorRefs.value[`${item.name}`]?.dispose(item.name);
  });
});
onUnmounted(() => {
  window.removeEventListener('keydown', handleEvent);
});
watch(
  () => appStore.windowState,
  () => {
    setReact();
  },
  { deep: true, immediate: true },
);
</script>
<template>
  <div ref="documentElRef" class="wos-notepad">
    <Toolbar :tab-pane="currentTabPane" @on-command="onCommand" />
    <div v-if="editableTabs.length === 0" class="wos-notepad__empty">
      <ElEmpty description="暂无文档" />
    </div>
    <ElTabs
      v-else
      v-model="editableTabsValue"
      closable
      size="small"
      type="border-card"
      @tab-click="clickTab"
      @tab-remove="removeTab"
    >
      <ElTabPane
        v-for="item in editableTabs"
        :key="item.name"
        :name="item.name"
        :title="item.path || item.label"
      >
        <template #label>
          <div :title="item.path || item.label" class="label">
            {{ item.label }}
          </div>
        </template>
        <Suspense>
          <template #default>
            <CodeEditor
              :ref="($el: any) => setEditorRef($el, item.name)"
              v-model:value="item.content"
              :ext="item.ext"
              :height="documentHeight"
              :name="item.name"
              :toolbar-height="72"
            />
          </template>
          <template #fallback>
            <div class="flex items-center justify-center h-full pt-12">
              Loading CodeEditor...
            </div>
          </template>
        </Suspense>
      </ElTabPane>
    </ElTabs>
    <Teleport to="body">
      <SelectByFileType ref="selectByFileTypeRef" @confirm="onConfirm" />
    </Teleport>
    <Teleport to="body">
      <SaveTo
        ref="saveToRef"
        @cancel="onSaveToCancel"
        @confirm="onSaveToConfirm"
      />
    </Teleport>
    <Teleport to="body">
      <!--更多设置-->
      <MonacoEditorSetting ref="moreSettingElRef" />
    </Teleport>
  </div>
</template>
<style lang="scss">
@import url('./css/notepad.scss');
</style>
